﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Fly.FastEval
{
    /// <summary>
    /// 获取指定对象的指定属性值
    /// </summary>
    public class DynamicPropertyAccessor
    {
        /// <summary>
        /// 委托
        /// </summary>
        private readonly Func<object, object> _mGetter;
        private readonly DynamicMethodExecutor _mDynamicSetter;
        /// <summary>
        /// 实例化对象
        /// </summary>
        /// <param name="type"></param>
        /// <param name="propertyName"></param>
        public DynamicPropertyAccessor(Type type, string propertyName): this(type.GetProperty(propertyName))
        { }
        /// <summary>
        /// 实例化对象
        /// </summary>
        /// <param name="propertyInfo"></param>
        public DynamicPropertyAccessor(PropertyInfo propertyInfo)
        {
            if (propertyInfo == null)
            {
                _mGetter = null;
                return;
            }
            // target: (object)((({TargetType})instance).{Property})

            // preparing parameter, object type
            ParameterExpression instance = Expression.Parameter(typeof(object), "instance");

            // ({TargetType})instance
            Expression instanceCast = Expression.Convert(instance, propertyInfo.ReflectedType);

            // (({TargetType})instance).{Property}
            Expression propertyAccess = Expression.Property(instanceCast, propertyInfo);

            // (object)((({TargetType})instance).{Property})
            UnaryExpression castPropertyValue = Expression.Convert(propertyAccess, typeof(object));

            // Lambda expression
            Expression<Func<object, object>> lambda = Expression.Lambda<Func<object, object>>(castPropertyValue, instance);

            _mGetter = lambda.Compile();

            MethodInfo setMethod = propertyInfo.GetSetMethod();
            if (setMethod != null)
            {
                _mDynamicSetter = new DynamicMethodExecutor(setMethod);
            }
        }
        /// <summary>
        /// 获取指定属性的值
        /// </summary>
        /// <param name="o"></param>
        /// <returns></returns>
        public object GetValue(object o)
        {
            if (_mGetter != null)
            {
                return _mGetter(o);
            }
            return null;
        }
        /// <summary>
        /// 设置指定属性的值
        /// </summary>
        /// <param name="o"></param>
        /// <param name="value"></param>
        public void SetValue(object o, object value)
        {
            if (_mDynamicSetter == null)
            {
                throw new NotSupportedException("Cannot set the property.");
            }

            _mDynamicSetter.Execute(o, new[] { value });
        }
    }
}
